﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using Csla.Data;
using System.Data;
using System.Threading;
using Csla.Validation;
using System.Data.Common;
using System.Text.RegularExpressions;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System.Security.Cryptography;
using Asah.Web.Encryption;

namespace Asah.SiteWorx.Commerce
{
    [Serializable()]
    public class PaymentDetail : BusinessBase<PaymentDetail>
    {

        #region Business Methods

        private int _orderId = 0;
        private PaymentMethod _paymentMethod = PaymentMethod.Visa;
        private string _nameOnCard = "";
        private string _cardNumber = "";
        private string _expirationMonth = "";
        private string _expirationYear = "";
        
        [System.ComponentModel.DataObjectField(true, true)]
        public int OrderID
        {
            get
            {
                CanReadProperty("OrderID", true);
                return _orderId;
            }
        }

        public PaymentMethod PaymentMethod
        {
            get
            {
                CanReadProperty("PaymentMethod", true);
                return _paymentMethod;
            }
            set
            {
                CanWriteProperty("PaymentMethod", true);
                if (_paymentMethod != value)
                {
                    _paymentMethod = value;
                    PropertyHasChanged("PaymentMethod");
                }
            }
        }

        public string NameOnCard
        {
            get
            {
                CanReadProperty("NameOnCard", true);
                return _nameOnCard;
            }
            set
            {
                CanWriteProperty("NameOnCard", true);
                if (_nameOnCard != value)
                {
                    _nameOnCard = value;
                    PropertyHasChanged("NameOnCard");
                }
            }
        }

        public string CardNumber
        {
            set
            {
                CanWriteProperty("CardNumber", true);
                if (_cardNumber != value)
                {
                    _cardNumber = value;
                    PropertyHasChanged("CardNumber");
                }
            }
        }

        public string ExpirationMonth
        {
            get
            {
                CanReadProperty("ExpirationMonth", true);
                return _expirationMonth;
            }
            set
            {
                CanWriteProperty("ExpirationMonth", true);
                if (_expirationMonth != value)
                {
                    _expirationMonth = value;
                    PropertyHasChanged("ExpirationMonth");
                }
            }
        }

        public string ExpirationYear
        {
            get
            {
                CanReadProperty("ExpirationYear", true);
                return _expirationYear;
            }
            set
            {
                CanWriteProperty("ExpirationYear", true);
                if (_expirationYear != value)
                {
                    _expirationYear = value;
                    PropertyHasChanged("ExpirationYear");
                }
            }
        }

        protected override object GetIdValue()
        {
            return _orderId;
        }

        #endregion

        #region Validation Rules

        protected override void AddBusinessRules()
        {
            // add BusinessRules here
            ValidationRules.AddInstanceRule(NameRequired, "NameOnCard");
            ValidationRules.AddInstanceRule(CardNumberRequired, "CardNumber");
            ValidationRules.AddInstanceRule(ValidateCardNumber, "CardNumber");
            ValidationRules.AddInstanceRule(ExpirationMonthRequired, "ExpirationMonth");
            ValidationRules.AddInstanceRule(ExpirationYearRequired, "ExpirationYear");
        }

        private bool NameRequired(object target, Csla.Validation.RuleArgs e)
        {
            if (_nameOnCard.Length > 0)
            {
                return true;
            }
            else
            {
                e.Description = "Name is required.";
                return false;
            }
        }

        private bool CardNumberRequired(object target, Csla.Validation.RuleArgs e)
        {
            if (_cardNumber.Length > 0)
            {
                return true;
            }
            else
            {
                e.Description = "Card number is required.";
                return false;
            }
        }

        private bool ValidateCardNumber(object target, Csla.Validation.RuleArgs e)
        {
            string cardNumber = _cardNumber;
            Regex expression = new Regex(@"^(\d{4}[- ]){3}\d{4}|\d{16}$");
            if (expression.IsMatch(_cardNumber))
            {
                cardNumber = cardNumber.Replace(" ", "");
                cardNumber = cardNumber.Replace("-", "");

                int length = cardNumber.Length;

                if (length < 13)
                    return false;
                int sum = 0;
                int offset = length % 2;
                byte[] digits = new System.Text.ASCIIEncoding().GetBytes(cardNumber);

                for (int i = 0; i < length; i++)
                {
                    digits[i] -= 48;
                    if (((i + offset) % 2) == 0)
                        digits[i] *= 2;

                    sum += (digits[i] > 9) ? digits[i] - 9 : digits[i];
                }

                if ((sum % 10) == 0)
                {
                    return true;
                }
                else
                {
                    e.Description = "Invalid card number.";
                    return false;
                }
            }
            else
            {
                e.Description = "Invalid card number.";
                return false;
            }
        }

        private bool ExpirationMonthRequired(object target, Csla.Validation.RuleArgs e)
        {
            if (_expirationMonth.Length > 0)
            {
                return true;
            }
            else
            {
                e.Description = "Expiration month is required.";
                return false;
            }
        }

        private bool ExpirationYearRequired(object target, Csla.Validation.RuleArgs e)
        {
            if (_expirationYear.Length > 0)
            {
                return true;
            }
            else
            {
                e.Description = "Expiration year is required.";
                return false;
            }
        }

        #endregion

        #region Authorization Rules

        protected override void AddAuthorizationRules()
        {
            // add AuthorizationRules here
        }

        public static bool CanAddPaymentDetail()
        {
            return true;
        }

        public static bool CanGetPaymentDetail()
        {
            return true;
        }

        public static bool CanDeletePaymentDetail()
        {
            return true;
        }

        public static bool CanEditPaymentDetail()
        {
            return false;
        }

        #endregion

        #region Factory Methods

        internal static PaymentDetail NewPaymentDetail()
        {
            return new PaymentDetail();
        }

        internal static PaymentDetail GetPaymentDetail(SafeDataReader dr)
        {
            return new PaymentDetail(dr);
        }

        private PaymentDetail()
        {
            MarkAsChild();
            ValidationRules.CheckRules();
        }

        private PaymentDetail(SafeDataReader dr)
        {
            MarkAsChild();
            Fetch(dr);
        }

        #endregion

        #region Data Access

        private void Fetch(SafeDataReader dr)
        {
            _orderId = dr.GetInt32("OrderId");
            _paymentMethod = (PaymentMethod)dr.GetInt32("PaymentMethod");
            _nameOnCard = dr.GetString("NameOnCard");
            _cardNumber = dr.GetString("CardNumber");
            _expirationMonth = dr.GetString("ExpirationMonth");
            _expirationYear = dr.GetString("ExpirationYear");

            MarkOld();
            ValidationRules.CheckRules();
        }

        internal void Insert(Order order)
        {
            // if we're not dirty then don't update the database
            if (!this.IsDirty) return;

            // first encrypt the card number.
            Symmetric crypto = new Symmetric(Symmetric.Provider.Rijndael);
            crypto.Key.Text = ";]ó”(¨Mêy¸¡ƒÜµ´…I£ªšÀð$g¬~c*";
            string cardNumber = crypto.Encrypt(new Data(_cardNumber)).Base64;


            Database db = DatabaseFactory.CreateDatabase("SiteWorxCommerce");
            DbCommand dbCommand = db.GetStoredProcCommand("aspnet_OrderPaymentDetails_AddPaymentDetail");
            db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
            db.AddInParameter(dbCommand, "OrderId", DbType.Int32, order.ID);
            db.AddInParameter(dbCommand, "PaymentMethod", DbType.Int32, _paymentMethod);
            db.AddInParameter(dbCommand, "NameOnCard", DbType.String, _nameOnCard);
            db.AddInParameter(dbCommand, "CardNumber", DbType.String, cardNumber);
            db.AddInParameter(dbCommand, "ExpirationMonth", DbType.String, _expirationMonth);
            db.AddInParameter(dbCommand, "ExpirationYear", DbType.String, _expirationYear);
            
            try
            {
                db.ExecuteNonQuery(dbCommand);
                dbCommand.Dispose();
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                if (rethrow)
                {
                    throw;
                }
            }

            MarkOld();
        }

        internal void DeleteSelf()
        {
            // if we're not dirty then don't update the database
            if (!this.IsDirty) return;

            // if we're new then don't update the database
            if (this.IsNew) return;

            Database db = DatabaseFactory.CreateDatabase("SiteWorxCommerce");
            DbCommand dbCommand = db.GetStoredProcCommand("aspnet_OrderPaymentDetails_RemovePaymentDetail");
            db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
            db.AddInParameter(dbCommand, "OrderId", DbType.Int32, this.OrderID);

            try
            {
                db.ExecuteNonQuery(dbCommand);
                dbCommand.Dispose();
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                if (rethrow)
                {
                    throw;
                }
            }

            MarkNew();
        }

        #endregion

    }
}
